﻿using System;
using System.Collections.Generic;
using System.Linq;
using Monopoly.Domain;
using Monopoly.Exceptions;

namespace Monopoly
{
    class Program
    {
        private enum Menus : short
        {
            Home = 0,
            SetupGame = 1,
            PlayGame = 2,
            PlayRound = 3,
            FieldManagement = 4,
            RealEstateManagement = 5,
            NoEnoughMoney = 6
        }

        private enum Home : short
        {
            PlayGame = 1,
            SetupGame = 2,
            Exit = 0
        }
        private enum SetupGame : short
        {
            AddPlayer = 1,
            Complete = 0
        }
        private enum PlayGame : short
        {
            PlayRound = 1,
            LeaveGame = 0
        }
        private enum PlayRound : short
        {
            FieldManagement = 1,
            RealEstateManagement = 2,
            ThrowDices = 3,
            Return = 0
        }
        private enum FieldManagement : short
        {
            SaleField = 1,
            BuyField = 2,
            Return = 0
        }
        private enum RealEstateManagement : short
        {
            SaleHouse = 1,
            SaleHotel = 2,
            BuyHouse = 3,
            BuyHotel = 4,
            Return = 0
        }
        private enum NoEnoughMoney : short
        {
            FieldManagement = 1,
            RealEstateManagement = 2,
            Pay = 3,
            QuitGame = 0
        }
        private enum TryToPay : short
        {
            FromCards = 1,
            FromTaxesBox = 2,
            FromBuyingBoxProperty = 3,
            FromBid = 4,
            PayOtherPlayer = 5,
            GetOutOfJail = 6,
            None = 0
        }

        protected static void QuitHandler(Object Sender, ConsoleCancelEventArgs Args)
        {
            Console.WriteLine("Vous avez pressé sur {0}", Args.SpecialKey);
            Console.WriteLine("Êtes-vous sûr de vouloir quitter (o/n) ?");
            ConsoleKeyInfo c = Console.ReadKey(true);
            if (c.KeyChar == 'o' || c.KeyChar == 'O')
                Args.Cancel = true;
            else
                Args.Cancel = false;
        }

        private static bool Autoplay = false;
        private static Menus CurrentMenu = Menus.Home;
        private static Game CurrentGame = null;
        private static IList<Game> Games = null;
        static void Main(string[] args)
        {
            Console.CancelKeyPress += new ConsoleCancelEventHandler(QuitHandler);

            bool quit = false;
            Games = new List<Game>();

            Console.Title = "Monopoly";
            WriteLine("Bienvenue sur le jeu Monopoly Console");
            while (!quit)
            {
                try
                {
                    Write("\n\nQue voulez-vous faire ?\n\n");
                    DisplayMenu();

                    PerformAction(int.Parse(Console.ReadKey(true).KeyChar.ToString()));
                }
                catch (Exception e)
                {
#if (DEBUG)
                    Console.Write(e);
#else
                    DisplayCancelAction("Une erreur inconnue est survenue");
#endif
                    Write("\nChoix invalide\n");
                }
            }
        }

        private static void PerformAction(int Action)
        {
            int Choice;
            switch (CurrentMenu)
            {
                case Menus.Home:
                    switch (Action)
                    {
                        case 0: //Quit application
                            Environment.Exit(0);
                            break;
                        case 1: //Play game
                            if (Games.Count == 0)
                            {
                                Autoplay = true;
                                Game NewGamePlay = new Game();
                                Games.Add(NewGamePlay);
                                CurrentGame = NewGamePlay;

                                CurrentMenu = Menus.SetupGame;
                            }
                            else
                                BeginGame();
                            break;
                        case 2: //Create game
                            Game NewGameCreate = new Game();
                            Games.Add(NewGameCreate);
                            CurrentGame = NewGameCreate;

                            CurrentMenu = Menus.SetupGame;
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    break;
                case Menus.SetupGame:
                    switch (Action)
                    {
                        case 0: //Quit game configuration menu
                            if (CurrentGame.HasPlayers())
                                if (Autoplay)
                                    BeginGame();
                                else
                                    CurrentMenu = Menus.Home;
                            else
                            {
                                DisplayCancelAction("Impossible de créer la partie (pas assez de joueurs)");

                                Games.Remove(CurrentGame);
                                CurrentGame = null;
                                CurrentMenu = Menus.Home;
                            }
                            break;
                        case 1: //Add a player
                            try
                            {
                                Write("\nNom du joueur : ");

                                string PlayerName = Console.ReadLine();
                                while (string.IsNullOrEmpty(PlayerName))
                                    PlayerName = Console.ReadLine();
                                if (!CurrentGame.AddPlayer(PlayerName))
                                {
                                    DisplayCancelAction("Nombre maximual de joueurs atteint.\nLa partie va commencer automatiquement avec les joueurs déjà présents.");
                                    if (Autoplay)
                                        BeginGame();
                                    else
                                        CurrentMenu = Menus.Home;
                                }
                            }
                            catch (PlayerNameAlreadyExistsException)
                            {
                                DisplayCancelAction("Ce nom de joueur existe déjà");
                            }
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    break;
                case Menus.PlayGame:
                    switch (Action)
                    {
                        case 0: // Quit game
                            QuitGame();
                            break;
                        case 1:// Play a round
                            CurrentMenu = Menus.PlayRound;
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    break;
                case Menus.PlayRound:
                    switch (Action)
                    {
                        case 0:
                            CurrentMenu = Menus.PlayGame;
                            break;
                        case 1:
                            CurrentMenu = Menus.FieldManagement;
                            break;
                        case 2:
                            CurrentMenu = Menus.RealEstateManagement;
                            break;
                        case 3:
                            CurrentGame.ThrowDices();
                            Move(CurrentGame.GetTotalDicesValue());

                            // Player has made a double and he is not in jail after the end of the turn
                            if (!CurrentGame.GetCurrentPlayer().GetIsInJail() && CurrentGame.IsDoubles() && CurrentGame.GetDoublesInRow() <= 2)
                                CurrentMenu = Menus.PlayRound;
                            else
                            {
                                Console.ReadKey(true);
                                CurrentGame.SetNextPlayer();
                            }
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    break;
                case Menus.FieldManagement:
                    ManageField(Action);
                    break;
                case Menus.RealEstateManagement:
                    IList<Field> FieldList;
                    int index;
                    Bank Bank = CurrentGame.GetBank();
                    switch (Action)
                    {
                        case 0: //Go back to play round menu
                            CurrentMenu = Menus.PlayRound;
                            break;
                        case 1: //Sale a house
                            index = 1;
                            //Displays field list
                            FieldList = CurrentGame.GetCurrentPlayer().GetFieldWithRealEstate(typeof(House));
                            if (FieldList.Count > 0)
                            {
                                FieldList = FieldList.OrderBy(f => f.GetId()).ToList();
                                Write("\nListe des terrains où vous pouvez vendre des maisons :\n\n");
                                foreach (BoxProperty Field in FieldList)
                                    WriteLine(index++ + " - " + Field.GetName(true));
                                Write("\n0 - Retour\n");

                                //Reads and computes user's choice
                                Choice = ReadChoice(index - 1);
                                if (Choice != 0)
                                    try
                                    {
                                        int InitialCash = CurrentGame.GetCurrentPlayer().GetCash();
                                        ((Field)FieldList[Choice - 1]).SaleRealEstate(Bank, typeof(House));
                                        WriteLine("L'opération a dégagé " + (CurrentGame.GetCurrentPlayer().GetCash() - InitialCash).ToEuro(true));
                                    }
                                    catch (Exception e)
                                    {
#if(DEBUG)
                                        Console.WriteLine(e);
#else
                                        DisplayCancelAction("Impossible de vendre cette maison");
#endif
                                    }
                            }
                            else
                                DisplayCancelAction("Aucun terrain diposant de maison à vendre n'a été trouvé");
                            CurrentMenu = Menus.PlayRound;
                            break;
                        case 2: //Sale a hotel
                            index = 1;
                            //Displays field list
                            FieldList = CurrentGame.GetCurrentPlayer().GetFieldWithRealEstate(typeof(Hotel));
                            if (FieldList.Count > 0)
                            {
                                FieldList = FieldList.OrderBy(f => f.GetId()).ToList();
                                Write("\nListe des terrains où vous pouvez vendre des hôtels :\n\n");
                                foreach (BoxProperty Field in FieldList)
                                    WriteLine(index++ + " - " + Field.GetName(true));
                                Write("\n0 - Retour\n");

                                //Reads and computes user's choice
                                Choice = ReadChoice(index - 1);
                                if (Choice != 0)
                                    try
                                    {
                                        int InitialCash = CurrentGame.GetCurrentPlayer().GetCash();

                                        Field Field = (Field)FieldList[Choice - 1];
                                        if (Bank.HasEnoughRealEstate(typeof(House), 4))
                                        {
                                            Field.SaleRealEstate(Bank, typeof(Hotel));
                                            Bank.BuyRealEstate(typeof(House), 4, Field);
                                            Write("\nL'hotel a été vendu et remplacé par 4 maisons\n");
                                        }
                                        else
                                        {
                                            //Bank is in a real estate crisis and doesn't have enough houses
                                            Bank.BuyMaxHouses(Field);

                                            //Display final result
                                            foreach (Field f in CurrentGame.GetCurrentPlayer().GetFieldListByColor(Field.GetColor()))
                                            {
                                                int HouseCount = f.GetHouseCount();
                                                WriteLine(HouseCount + (HouseCount > 1 ? " houses" : " house") + " on " + f.GetName(true));
                                            }
                                            WriteLine();
                                            int RemainingAvailableHouses = Bank.GetRemainingAvailableRealEstate(typeof(House));
                                            if (RemainingAvailableHouses > 0)
                                                WriteLine("la banque propose encore " + RemainingAvailableHouses + (RemainingAvailableHouses > 1 ? " maisons" : " maison"));
                                        }
                                        WriteLine("L'opération a dégagé " + (CurrentGame.GetCurrentPlayer().GetCash() - InitialCash).ToEuro(true));
                                    }
                                    catch (Exception)
                                    {
                                        DisplayCancelAction("Impossible de vendre cet hôtel");
                                    }
                            }
                            else
                                DisplayCancelAction("Vous n'avez aucun terrain disposant d'hôtel à vendre");
                            CurrentMenu = Menus.PlayRound;
                            break;
                        case 3: //Buy a house
                            index = 1;
                            if (!Bank.HasEnoughRealEstate(typeof(House)))
                            {
                                DisplayCancelAction("Crise de l'immobilier, impossible d'acheter une maison");
                                CurrentMenu = Menus.PlayRound;
                            }
                            else
                            {
                                //Display field list
                                FieldList = CurrentGame.GetCurrentPlayer().CanAddRealEstateList(typeof(House));
                                if (FieldList.Count > 0)
                                {
                                    FieldList = FieldList.OrderBy(f => f.GetId()).ToList();
                                    Write("\nListe des terrains où vous pouvez acheter des maisons :\n\n");
                                    foreach (BoxProperty Field in FieldList)
                                        WriteLine(index++ + " - " + Field.GetName(true));
                                    Write("\n0 - Retour\n");

                                    //Reads and computes user's choice
                                    Choice = ReadChoice(index - 1);
                                    if (Choice != 0)
                                        try
                                        {
                                            int InitialCash = CurrentGame.GetCurrentPlayer().GetCash();
                                            Bank.BuyRealEstate(typeof(House), ((Field)FieldList[Choice - 1]));
                                            WriteLine("L'opération a consommé " + (InitialCash - CurrentGame.GetCurrentPlayer().GetCash()).ToEuro(false));
                                        }
                                        catch (Exception e)
                                        {
#if (DEBUG)
                                            Console.Write(e);
#else
                                            DisplayCancelAction("Une erreur est survenue lors de l'achat d'une maison");
#endif
                                        }
                                    else
                                        DisplayCancelAction("Vous avez annulé l'achat d'une maison");
                                }
                                else
                                    DisplayCancelAction("Vous ne disposez d'aucun terrain sur lequel il est possible d'ajouter une maison");

                                CurrentMenu = Menus.PlayRound;
                            }
                            break;
                        case 4: //Buy a hotel
                            index = 1;
                            if (!Bank.HasEnoughRealEstate(typeof(House)))
                            {
                                DisplayCancelAction("Crise de l'immobilier, impossible d'acheter un hotel");
                                CurrentMenu = Menus.PlayRound;
                            }
                            else
                            {
                                //Display field list
                                FieldList = CurrentGame.GetCurrentPlayer().CanAddRealEstateList(typeof(Hotel));
                                if (FieldList.Count > 0)
                                {
                                    FieldList = FieldList.OrderBy(f => f.GetId()).ToList();
                                    Write("\nListe des terrains où vous pouvez acheter un hotel :\n\n");
                                    foreach (BoxProperty Field in FieldList)
                                        WriteLine(index++ + " - " + Field.GetName(true));
                                    Write("\n0 - Retour\n");

                                    //Reads and computes user's choice
                                    Choice = ReadChoice(index - 1);
                                    if (Choice != 0)
                                        try
                                        {
                                            int InitialCash = CurrentGame.GetCurrentPlayer().GetCash();
                                            Bank.BuyRealEstate(typeof(Hotel), ((Field)FieldList[Choice - 1]));
                                            WriteLine("L'opération a consommé " + (InitialCash - CurrentGame.GetCurrentPlayer().GetCash()).ToEuro(false));
                                        }
                                        catch (Exception e)
                                        {
#if (DEBUG)
                                            Console.Write(e);
#else
                                            DisplayCancelAction("Une erreur est survenue lors de l'achat d'un hotel");
#endif
                                        }
                                    else
                                        DisplayCancelAction("Vous avez anunulé l'achat d'un hotel");
                                }
                                else
                                    DisplayCancelAction("Vous ne disposez d'aucun terrain sur lequel il est possible d'ajouter un hotel");
                                CurrentMenu = Menus.PlayRound;
                            }
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    break;
                case Menus.NoEnoughMoney:
                    switch (Action)
                    {
                        case 0:
                            QuitGame();
                            break;
                        case 1:
                            CurrentMenu = Menus.FieldManagement;
                            break;
                        case 2:
                            CurrentMenu = Menus.RealEstateManagement;
                            break;
                        case 3:

                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                    break;
            }
        }

        /// <summary>
        /// Displays available actions in the current menus
        /// </summary>
        private static void DisplayMenu()
        {
            switch (CurrentMenu)
            {
                case Menus.Home:
                    WriteLine("1 - Jouer une partie");
                    WriteLine("2 - Configurer une partie");
                    WriteLine("0 - Quitter l'application");
                    break;
                case Menus.SetupGame:
                    WriteLine("1 - Ajouter un joueur");
                    WriteLine("0 - Terminer la configuration");
                    break;
                case Menus.PlayGame:
                    WriteLine("1 - Jouer le tour");
                    WriteLine("0 - Quitter la partie");
                    break;
                case Menus.PlayRound:
                    WriteLine("1 - Gestion foncière");
                    WriteLine("2 - Gestion immobilière");
                    WriteLine("3 - Jeter les dés");
                    WriteLine("0 - Retour");
                    break;
                case Menus.FieldManagement:
                    WriteLine("1 - Vendre un terrain");
                    WriteLine("2 - Acheter un terrain");
                    WriteLine("0 - Retour");
                    break;
                case Menus.RealEstateManagement:
                    WriteLine("1 - Vendre une maison");
                    WriteLine("2 - Vendre un hotel");
                    WriteLine("3 - Acheter une maison");
                    WriteLine("4 - Acheter un hôtel");
                    WriteLine("0 - Retour");
                    break;
                case Menus.NoEnoughMoney:
                    WriteLine("1 - Gestion foncière");
                    WriteLine("2 - Gestion immobilière");
                    WriteLine("3 - Payez");
                    WriteLine("0 - Quitter la partie");
                    break;
            }
        }

        /// <summary>
        /// Start a the current game with a randomly choised player
        /// </summary>
        private static void BeginGame()
        {
            CurrentGame.BeginGame();
            CurrentMenu = Menus.PlayGame;
        }

        /// <summary>
        /// Allow a player to leave the game.
        /// </summary>
        /// <remarks>The game continues until there is only one player left.</remarks>
        private static void QuitGame()
        {
            Player Leaver = CurrentGame.GetCurrentPlayer();

            Bank Bank = CurrentGame.GetBank();
            IList<RealEstate> LeaverRealEstates = Leaver.RemoveRealEstates();
            foreach (RealEstate RealEstate in LeaverRealEstates)
                if (RealEstate is Hotel)
                    Bank.AddHotel((Hotel)RealEstate);
                else
                    Bank.AddHouse((House)RealEstate);

            IList<BoxProperty> LeaverProperties = Leaver.RemoveProperties();
            Bank.AddProperties(LeaverProperties);

            IList<Card> LeaverCards = Leaver.RemoveCards();
            CurrentGame.GetGameboard().AddCards(LeaverCards);

            Write("\n" + Leaver.GetName() + " a quitté la partie\n");
            CurrentGame.RemoveCurrentPlayer();
            if (CurrentGame.HasPlayers())
                CurrentMenu = Menus.PlayGame;
            else
                DeclareWinner();
        }

        public static void ManageField(int Action)
        {
            int Choice;
            IList<Player> PlayerList;
            IList<BoxProperty> Properties;

            switch (Action)
            {
                case 0:
                    //Return
                    CurrentMenu = Menus.PlayRound;
                    break;
                case 1:
                    //Sale field
                    //Display field list
                    WriteLine("\nChoisissez votre propriété à vendre : ");
                    Properties = CurrentGame.GetCurrentPlayer().GetProperties().Where(p => (p is Field && !((Field)p).HasRealEstate(typeof(House)) && !((Field)p).HasRealEstate(typeof(Hotel))) || !(p is Field)).OrderBy(p => p.GetId()).ToList<BoxProperty>();

                    if (Properties.Count > 0)
                    {
                        for (int i = 1; i <= Properties.Count; i++)
                            WriteLine(i + " - " + Properties[i - 1].GetName(true));
                        Write("\n0 - Retour\n");

                        //Read and compute user's choice
                        Choice = ReadChoice(Properties.Count);
                        if (Choice != 0)
                        {
                            //Display player list
                            BoxProperty TmpProperty = Properties[Choice - 1];
                            WriteLine("Choisissez le joueur auquel vous désirez vendre " + TmpProperty.GetName(true) + " :");
                            PlayerList = CurrentGame.GetPlayers().ToList<Player>();
                            PlayerList.Remove(CurrentGame.GetCurrentPlayer());
                            for (int i = 1; i <= PlayerList.Count; i++)
                                WriteLine(i + " - " + PlayerList[i - 1].GetName());
                            Write("\n0 - Retour\n");

                            //Read and compute user's choice
                            Choice = ReadChoice(PlayerList.Count);
                            if (Choice != 0)
                            {
                                //Display confirmation options
                                WriteLine((PlayerList[Choice - 1]).GetName().Colorize(ConsoleColor.White) + " souhaite-t-il acheter " + TmpProperty.GetName(true) + " pour " + TmpProperty.GetPrice().ToEuro() + " ?");
                                WriteLine("1 - Oui");
                                WriteLine("2 - Non");
                                WriteLine("0 - Retour");

                                switch (ReadChoice(2))
                                {
                                    case 0: //Return
                                        break;
                                    case 1: //Sale
                                        Player TmpPlayer = PlayerList[Choice - 1];
                                        CurrentGame.GetCurrentPlayer().SellProperty(TmpPlayer, TmpProperty);
                                        WriteLine(TmpProperty.GetName(true) + " a été vendu à " + TmpPlayer.GetName());
                                        break;
                                    case 2: //Cancel
                                        DisplayCancelAction((PlayerList[Choice - 1]).GetName() + " a refusé l'achat de votre terrain");
                                        break;
                                }
                            }
                            else
                                DisplayCancelAction("Vous avez annulé la vente d'un terrain");
                        }
                        else
                            DisplayCancelAction("Vous avez annulé la vente d'un terrain");
                    }
                    else
                        DisplayCancelAction("Aucun terrain disponible à la vente.");

                    CurrentMenu = Menus.PlayRound;
                    break;
                case 2: //Buy a BoxProperty to another player
                    PlayerList = CurrentGame.GetPlayersWithFields();
                    PlayerList.Remove(CurrentGame.GetCurrentPlayer());
                    if (PlayerList.Count > 0)
                    {
                        //Display player list
                        Write("\nÀ quel joueur acheter le terrain :\n\n");
                        for (int i = 1; i <= PlayerList.Count; i++)
                            WriteLine(i + " - " + PlayerList[i - 1].GetName());
                        Write("\n0 - Retour\n");

                        //Read user's choice
                        Choice = ReadChoice(PlayerList.Count);

                        //Compute user choice
                        if (Choice != 0)
                        {
                            Player TmpPlayer = (PlayerList[Choice - 1]);
                            Properties = TmpPlayer.GetProperties().Where(p => (p is Field && !((Field)p).HasRealEstate(typeof(House)) && !((Field)p).HasRealEstate(typeof(Hotel))) || !(p is Field)).OrderBy(p => p.GetId()).ToList<BoxProperty>();

                            //Display BoxProperty list
                            Write("\nQuel terrain avez-vous convenu d'acheter :\n\n");
                            for (int i = 1; i <= Properties.Count; i++)
                                WriteLine(i + " - " + Properties[i - 1].GetName(true));
                            Write("\n0 - Retour\n");

                            //Read user's choice
                            Choice = ReadChoice(Properties.Count);
                            if (Choice != 0)
                            {
                                BoxProperty TmpProperty = Properties[Choice - 1];
                                TmpPlayer.SellProperty(CurrentGame.GetCurrentPlayer(), TmpProperty);
                                WriteLine("Vous avez acheté " + TmpProperty.GetName(true));
                            }
                            else
                                DisplayCancelAction("Vous avez annulé l'achat d'un terrain");
                        }
                        else
                            DisplayCancelAction("Vous avea annulé l'achat d'un terrain");
                    }
                    else //aboard
                        DisplayCancelAction("Impossible de trouver un joueur disposant d'un terrain à vendre");

                    CurrentMenu = Menus.PlayRound;
                    break;
                default:
                    throw new InvalidOperationException();
            }
        }

        public static void Move(int MoveValue, bool OnBox = false, bool StandBack = false)
        {
            Player CurrentPlayer = CurrentGame.GetCurrentPlayer();
            int Choice = -1;

            if (CurrentPlayer.GetIsInJail()) // Player is currently in jail
            {
                PerformJailAction(CurrentPlayer, MoveValue, false);
            }
            else if (CurrentGame.GetDoublesInRow() > 2) // He did more than 2 doubles
            {
                WriteLine("Vous avez réalisé 3 doublets consécutifs\n");
                CurrentPlayer.GoToJail();
            }
            else // Classic move
            {
                Box Box;
                if (OnBox)
                    Box = CurrentPlayer.ChangePosition(MoveValue);
                else if (StandBack)
                    Box = CurrentPlayer.MovePlayer(MoveValue, true);
                else
                    Box = CurrentPlayer.MovePlayer(MoveValue);

                if (Box is BoxProperty) // if box is a property
                {
                    BoxProperty BoxProperty = (BoxProperty)Box;
                    BoxPropertyOwner BoxPropertyOwner = BoxProperty.GetOwner();
                    if (BoxPropertyOwner is Bank) // box is free to buy
                    {
                        WriteLine("\nCette propriété est libre. Que voulez-vous faire ?");
                        WriteLine("1 - Acheter pour " + BoxProperty.GetPrice().ToEuro());
                        WriteLine("0 - Mettre aux enchères");
                        Choice = ReadChoice(2);
                        if (Choice == 1) // buy
                        {
                            if (!CurrentPlayer.BuyFreeProperty(BoxProperty))
                            {
                                throw new NotImplementedException(); // Not enough cash
                            }
                        }
                        else // bid
                        {
                            bool Winner = false;
                            int CurrentBid = BoxProperty.GetPrice();

                            IList<Player> Bidders = new List<Player>();
                            foreach (Player Player in CurrentGame.GetPlayers().Where(p => p != CurrentPlayer))
                                Bidders.Add(Player);
                            Bidders.Insert(0, CurrentPlayer); // Add the the current player at the top of the list
                            int Rounds = 0;
                            Write("\n" + BoxProperty.GetName(true) + " a été mise aux enchères pour " + CurrentBid.ToEuro() + "\n0 - Quitter l'enchère\n\n");
                            while (!Winner)
                            {
                                IList<Player> TmpBidders = new List<Player>();
                                TmpBidders = Bidders.ToList();
                                Rounds++;
                                foreach (Player CurrentBidder in Bidders)
                                {
                                    if (Rounds == 1)
                                    {
                                        CurrentBid = BidDisplay(CurrentBid, CurrentBidder, TmpBidders);
                                    }
                                    else if (TmpBidders.Count != 1)
                                    {
                                        CurrentBid = BidDisplay(CurrentBid, CurrentBidder, TmpBidders);
                                    }
                                }
                                Bidders = TmpBidders.ToList();
                                if (Bidders.Count == 1)
                                {
                                    Winner = true;
                                    Bidders[0].BuyPropertyFromBid(BoxProperty, CurrentBid);
                                }
                                else if (Bidders.Count == 0)
                                {
                                    Winner = true; // if no one buys
                                    WriteLine("Aucun des joueurs n'a remporté l'enchère");
                                }
                            }
                        }

                    }
                    else if (BoxPropertyOwner is Player && BoxPropertyOwner != CurrentPlayer) // box belongs to an other player
                    {
                        Player Owner = (Player)BoxPropertyOwner;
                        WriteLine("Cette propriété appartient à " + Owner.GetName());

                        if (BoxProperty is Field) // box is a field
                        {
                            Field Field = (Field)BoxProperty;
                            if (!CurrentPlayer.PayPlayer(Owner, Field.GetRent(Owner.HasAllFieldsByColor(Field.GetColor()))))
                            {
                                throw new NotImplementedException(); // Not enough cash
                            }
                        }
                        else if (BoxProperty is Station) // box is a station
                        {
                            Station Station = (Station)BoxProperty;
                            if (!CurrentPlayer.PayPlayer(Owner, Station.GetRent(Owner.CountStation())))
                            {
                                throw new NotImplementedException(); // Not enough cash
                            }
                        }
                        else // box is a company
                        {
                            Company Company = (Company)BoxProperty;
                            if (!CurrentPlayer.PayPlayer(Owner, Company.GetRent(MoveValue, Owner.HasBothCompanies())))
                            {
                                throw new NotImplementedException(); // Not enough cash
                            }
                        }
                    }
                }
                else // box is not a a property
                {
                    BoxAction BoxAction = (BoxAction)Box;
                    BoxActionType BoxActionType = BoxAction.GetBoxActionType();
                    switch (BoxActionType)
                    {
                        case BoxActionType.PickAChanceCard:
                            Card ChanceCard = CurrentPlayer.PickAChanceCard();
                            WriteLine("\nVous avez tiré une carte chance : \n\t" + ChanceCard.GetText() + "\n");
                            PerformCardAction(ChanceCard);
                            break;
                        case BoxActionType.PickACommunityChestCard:
                            Card CommunityChestCard = CurrentPlayer.PickACommunityChestCard();
                            WriteLine("Vous avez tiré une carte caisse de communauté : \n\t" + CommunityChestCard.GetText() + "\n");
                            PerformCardAction(CommunityChestCard);
                            break;
                        case BoxActionType.VisitJail:
                            WriteLine("Vous visitez la prison\n");
                            break;
                        case BoxActionType.FreeParking:
                            CurrentGame.GetBank().GiveFreeParkingAmount(CurrentPlayer);
                            break;
                        case BoxActionType.GoToJail:
                            CurrentPlayer.GoToJail();
                            break;
                        case BoxActionType.PayATax:
                            int TaxAmount = -1;
                            if (BoxAction.GetName().Equals("Impôts sur le revenu"))
                                TaxAmount = 20000;
                            else
                                TaxAmount = 10000;
                            CurrentPlayer.PayBankForFreeParking(TaxAmount);
                            break;
                    }
                }

            }

        }

        public static void PerformJailAction(Player CurrentPlayer, int MoveValue, bool CancelCard = false)
        {
            int RoundsInJail = CurrentPlayer.GetRoundsInJail();
            if (CurrentGame.IsDoubles()) // if he makes a double
            {
                CurrentPlayer.GetOutOfJail();
                Move(MoveValue);
            }
            else if (CurrentPlayer.HasOutOfJailCard() && !CancelCard) // if he has a out of jail card
            {
                WriteLine("Vous avez une carte pour sortir de prison. Voulez-vous l'utiliser ?");
                WriteLine("1 - Oui");
                WriteLine("0 - Non");
                int Choice = ReadChoice(2);
                if (Choice == 1) // he decides to use his card
                {
                    CurrentPlayer.RemoveOutOfJailCard();
                    CurrentPlayer.GetOutOfJail();
                    Move(MoveValue);
                }
                else
                {  // Re-init turn
                    PerformJailAction(CurrentPlayer, MoveValue, true);
                }
            }
            else if (RoundsInJail <= 1) // He stays in jail
            {
                CurrentPlayer.IncrementRoundsInJail();
                RoundsInJail = CurrentPlayer.GetRoundsInJail();
                WriteLine("Vous avez passé " + RoundsInJail + (RoundsInJail > 1 ? " tours" : " tour") + " en prison\n");
            }
            else // He has spend 3 rounds in jail
            {
                WriteLine("Vous avez passé 3 tours en prison. Payez " + (5000).ToEuro(false) + " pour sortir");
                if (!CurrentPlayer.PayBankForFreeParking(5000))
                    throw new NotImplementedException(); // Not enough cash
                else
                {
                    CurrentPlayer.GetOutOfJail();
                    Move(MoveValue);
                }
            }

        }

        public static void PerformCardAction(Card Card)
        {
            Player CurrentPlayer = CurrentGame.GetCurrentPlayer();
            CardActionType CardActionType = Card.GetCardActionType();
            switch (CardActionType)
            {
                case CardActionType.PayFreeParking:
                    if (!CurrentPlayer.PayBankForFreeParking(Card.GetValue()))
                        throw new NotImplementedException();
                    break;
                case CardActionType.PayBank:
                    CurrentPlayer.PayBank(Card.GetValue());
                    break;
                case CardActionType.ReceiveBankMoney:
                    CurrentGame.GetBank().GiveCash(Card.GetValue(), CurrentPlayer);
                    break;
                case CardActionType.ReceivePlayerMoney:
                    foreach (Player Player in CurrentGame.GetPlayers().Where(p => p != CurrentPlayer))
                    {
                        if (!Player.PayPlayer(CurrentPlayer, 5000))
                        {
                            throw new NotImplementedException();
                        }
                    }
                    break;
                case CardActionType.GoToJail:
                    CurrentPlayer.GoToJail();
                    break;
                case CardActionType.WalkFreeOutOfJail:
                    CurrentPlayer.AddCard(Card);
                    break;
                case CardActionType.MoveOnBox:
                    Move(Card.GetValue(), true);
                    break;
                case CardActionType.Move:
                    if (Card.GetText().Equals("Reculez de trois cases"))
                        Move(Card.GetValue(), false, true);
                    else
                        Move(Card.GetValue());
                    break;
            }
        }

        public static int BidDisplay(int CurrentBid, Player CurrentBidder, IList<Player> TmpBidders)
        {
            if (!CurrentBidder.HasEnoughCash(CurrentBid))
            {
                WriteLine(CurrentBidder.GetName() + " n'a pas assez d'argent : il quitte l'enchère");
                TmpBidders.Remove(CurrentBidder);
            }
            else
            {
                WriteLine(CurrentBidder.GetName() + ", c'est à vous d'enchérir (montant actuel = " + CurrentBid.ToEuro() + ") :");
                int Bid = ReadBid(CurrentBidder.GetCash());
                if (Bid == 0)
                {
                    TmpBidders.Remove(CurrentBidder);
                    WriteLine(CurrentBidder.GetName() + " a quitté l'enchère");
                }
                else if (Bid == -1)
                {
                    TmpBidders.Remove(CurrentBidder);
                    WriteLine(CurrentBidder.GetName() + " n'a pas assez d'argent pour l'enchère qu'il propose : il quitte l'enchère");
                }
                else
                    CurrentBid += Bid;
            }
            return CurrentBid;
        }

        /// <summary>
        /// Write the name of the winner and display the right menu
        /// </summary>
        public static void DeclareWinner()
        {
            WriteLine(CurrentGame.GetCurrentPlayer().GetName() + " a gagné la partie", ConsoleColor.Green);
            Console.Beep(1600, 250);
            Console.Beep(200, 800);
            Console.Beep(400, 400);
            Console.Beep(1600, 250);
            Console.Beep(4800, 125);
            Console.Beep(400, 400);
            Console.Beep(1600, 250);
            Console.Beep(200, 800);
            Console.Beep(400, 400);
            Console.Beep(1600, 250);
            Games.Remove(CurrentGame);
            CurrentGame = null;
            CurrentMenu = Menus.Home;
        }


        public static int ReadBid(int PlayerCash)
        {
            while (true)
                try
                {
                    int res = int.Parse(Console.ReadLine());
                    if (res < PlayerCash)
                        return res;
                    else
                        return -1;
                }
                catch (Exception)
                {
                    Write("\nChoix invalide\n");
                }
        }


        /// <summary>
        /// Read a number used in a menu
        /// </summary>
        /// <param name="MaxValue">Maximal wanted value</param>
        /// <returns>Read integer</returns>
        public static int ReadChoice(int MaxValue = 0)
        {
            while (true)
                try
                {
                    int res = int.Parse(Console.ReadKey(true).KeyChar.ToString());
                    if (MaxValue != 0)
                    {
                        if (res <= MaxValue)
                            return res;
                    }
                    else
                        return res;
                }
                catch (Exception)
                {
                    Write("\nChoix invalide\n");
                }
        }

        public static Player GetCurrentPlayer()
        {
            return CurrentGame.GetCurrentPlayer();
        }










        /// <summary>
        /// Display the given message to the user. Add a text that explain the requested operation has been canceled.
        /// </summary>
        /// <param name="Message"></param>
        public static void DisplayCancelAction(string Message)
        {
            WriteLine(Message);
            WriteLine("Action annulée", ConsoleColor.Red);
            Console.ReadKey(true);
        }
        /// <summary>
        /// Write a new line
        /// </summary>
        public static void WriteLine()
        {
            Program.Write(Environment.NewLine);
        }
        /// <summary>
        /// Write the given text
        /// </summary>
        /// <param name="Text">Text to write</param>
        public static void Write(string Text)
        {
            Program.WriteColor(Text);
        }
        /// <summary>
        /// Write the given text an add a new line
        /// </summary>
        /// <param name="Text">Text to write</param>
        public static void WriteLine(string Text)
        {
            Program.Write(Text + Environment.NewLine);
        }
        /// <summary>
        /// Write the given text in the given color
        /// </summary>
        /// <param name="Text">Text to write</param>
        /// <param name="Color">Color of the text</param>
        public static void Write(string Text, ConsoleColor Color)
        {
            Program.WriteColor(Text.Colorize(Color));
        }
        /// <summary>
        /// Write the given text in the given color an add a new line
        /// </summary>
        /// <param name="Text">Text to write</param>
        /// <param name="Color">Color of the text</param>
        public static void WriteLine(string Text, ConsoleColor Color)
        {
            Program.Write(Text + Environment.NewLine, Color);
        }
        /// <summary>
        /// Write the given preformated text in colors
        /// </summary>
        /// <param name="Text">Preformated text</param>
        private static void WriteColor(string Text)
        {
            ConsoleColor OldColor = Console.ForegroundColor;
            string[] Texts = Text.Split(new char[] { '<', '>' });
            foreach (string s in Texts)
            {
                if (s.StartsWith("/"))
                {
                    Console.ForegroundColor = OldColor;
                    continue;
                }
                if (Enum.GetNames(typeof(ConsoleColor)).Contains(s))
                {
                    Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), s);
                    continue;
                }
                Console.Write(s);
            }
        }
        /// <summary>
        /// Write the given preformated text in colors and add a new line
        /// </summary>
        /// <param name="Text">Preformated text</param>
        private static void WriteLineColor(string Text)
        {
            WriteColor(Text + Environment.NewLine);
        }
    }
}
